/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.jsonser.builtinimpls;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.manager.serialize.JsonFormatType;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.AssoInfoBase;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.*;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.spi.jsonser.entity.AbstractEntitySerializerItem;
import com.inspur.edp.cef.spi.jsonser.util.SerializerUtil;
import com.inspur.edp.udt.entity.ISimpleUdtData;

import java.io.IOException;

import java.util.HashMap;
import java.util.Map;

public class CefEntityDataSerializerItem extends AbstractEntitySerializerItem {

    private CefEntityResInfoImpl resInfo;

    public CefEntityDataSerializerItem(CefEntityResInfoImpl resInfo){
        this.resInfo = resInfo;
    }

    @Override
    public void writeEntityBasicInfo(JsonGenerator jsonGenerator, IEntityData data, SerializerProvider serializerProvider) {
        for(DataTypePropertyInfo propertyInfoInfo :resInfo.getEntityTypeInfo().getPropertyInfos().values()){
            propertyInfoInfo.write(jsonGenerator, data.getValue(propertyInfoInfo.getPropertyName()), serializerProvider, getCefSerializeContext());
        }
    }

    @Override
    public boolean readEntityBasicInfo(JsonParser reader, DeserializationContext ctxt, IEntityData data, String propertyName) {
        if(!resInfo.getEntityTypeInfo().containsPropertyInfo(propertyName))
            return false;
        DataTypePropertyInfo info = resInfo.getEntityTypeInfo().getPropertyInfo(propertyName);
        Object value = info.read(reader, ctxt, getCefSerializeContext());
        data.setValue(info.getPropertyName(), value);
        return true;
    }

    @Override
    public boolean writeModifyPropertyJson(JsonGenerator jsonGenerator, String propertyName, Object value, SerializerProvider serializerProvider) {
        if(!resInfo.getEntityTypeInfo().containsPropertyInfo(propertyName))
            return false;
        DataTypePropertyInfo info = resInfo.getEntityTypeInfo().getPropertyInfo(propertyName);
        info.writeChange(jsonGenerator, value, serializerProvider, getCefSerializeContext());
        return true;
    }

    @Override
    public Object readModifyPropertyValue(JsonParser reader, DeserializationContext serializer, RefObject<String> propertyName, RefObject<Boolean> hasRead){
        if(!resInfo.getEntityTypeInfo().containsPropertyInfo(propertyName.argvalue))
            return null;
        DataTypePropertyInfo info = resInfo.getEntityTypeInfo().getPropertyInfo(propertyName.argvalue);
        Object value = info.readChange(reader, serializer, getCefSerializeContext());
        propertyName.argvalue = info.getPropertyName();
        hasRead.argvalue = true;
        return value;
    }

    @Override
    public Object readModifyPropertyValue(JsonParser p, DeserializationContext ctxt, HashMap<String, Object> changeValues, RefObject<String> propertyName, RefObject<Boolean> hasRead, CefSerializeContext context) {
        String propName = propertyName.argvalue;
        if(resInfo.getEntityTypeInfo().containsPropertyInfo(propName)){
            DataTypePropertyInfo info = resInfo.getEntityTypeInfo().getPropertyInfo(propName);
            if(info instanceof RefDataTypePropertyInfo&&((RefDataTypePropertyInfo) info).getBelongPropertyInfo()!=null&&((RefDataTypePropertyInfo) info).getBelongPropertyInfo().getObjectInfo() instanceof AssocationPropertyInfo)
            {
                if(changeValues.containsKey(((RefDataTypePropertyInfo) info).getBelongPropertyInfo().getPropertyName()))
                {
                    AssoInfoBase assoInfoBase= (AssoInfoBase) changeValues.get(((RefDataTypePropertyInfo) info).getBelongPropertyInfo().getPropertyName());
                    Object value =getValueFromPropertyInfo(info, p, ctxt, changeValues, propertyName, hasRead, context);
                    if(assoInfoBase!=null) {
                     if(value instanceof ValueObjModifyChangeDetail)
                     {
                         //临时兼容，需要尽快改。
                         assoInfoBase.setValue(info.getPropertyName(), null);
                     }
                     else
                        assoInfoBase.setValue(info.getPropertyName(), value);
                    }
                    return value;
                }
            }
            else {
                if (changeValues.containsKey(info.getPropertyName())) {
                    Object value = changeValues.get(info.getPropertyName());
                    try {
                        info.setValue(value, info.getPropertyName(), p.readValueAsTree(), context);
                        return info;
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return getValueFromPropertyInfo(info, p, ctxt, changeValues, propertyName, hasRead, context);
        }

        String[] names = propName.split("_", 2);
        String parentName = names[0];
        if(!resInfo.getEntityTypeInfo().containsPropertyInfoByLower(parentName.toLowerCase())){
            return null;
        }
        String realName = names[1];
        DataTypePropertyInfo info = resInfo.getEntityTypeInfo().getPropertyInfoByLower(parentName.toLowerCase());
        Object value = null;
        if(!changeValues.containsKey(info.getPropertyName())){
            value = info.createChange();
            changeValues.put(info.getPropertyName(), value);
        }else{
            value = changeValues.get(info.getPropertyName());
        }

        try {
            info.setValue(value, propName, p.readValueAsTree(), context);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        propertyName.argvalue = info.getPropertyName();
        hasRead.argvalue = true;
        return value;
    }

    private Object getValueFromPropertyInfo(DataTypePropertyInfo info, JsonParser p, DeserializationContext ctxt, HashMap<String, Object> changeValues, RefObject<String> propertyName, RefObject<Boolean> hasRead, CefSerializeContext context){
        //todo 后续把CefSerializeContext的构造全部放到外部，不在内部构造
        CefSerializeContext serContext = getCefSerializeContext();
        serContext.setJsonFormatType(context.getJsonFormatType());
        Object value = info.readChange(p, ctxt, getCefSerializeContext());
        propertyName.argvalue = info.getPropertyName();
        hasRead.argvalue = true;
        if(info.getObjectType() != ObjectType.Normal){
            changeValues.put(propertyName.argvalue, value);
        }
        return value;
    }

    @Override
    public Map<String, String> getTransferPropertyNameMap() {
        Map<String, String> map=new HashMap<>();
        for(DataTypePropertyInfo propertyInfo:resInfo.getEntityTypeInfo().getPropertyInfos().values())
        {
            if(propertyInfo.getObjectType()== ObjectType.UDT)
                continue;
            map.put(StringUtils.toCamelCase(propertyInfo.getPropertyName()),propertyInfo.getPropertyName());
        }
        return map;
    }

    public CefEntityResInfoImpl getResInfo() {
        return resInfo;
    }
}
